import os
import sys
import argparse
import logging
import traceback
from datetime import datetime
from typing import Optional, List
from dotenv import load_dotenv

# 导入各个模块
from get_tools_from_github import fetch_repositories, save_to_csv, HEADERS, query_params
from get_pypi_name import FileProcessor
from install_pypi_targets import PyPiInstaller
from auto_verify_pypi import AutoVerifier

# 加载.env文件中的环境变量
load_dotenv()

# 配置
GITHUB_TOKEN = os.getenv("GITHUB_ACCESS_TOKEN")

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class DeployPipeline:
    """AI工具部署流水线"""
    
    def __init__(
        self, 
        topic: str = "ai",
        min_stars: int = 5000,
        max_repos: int = 100,
        github_token: str = GITHUB_TOKEN,
        use_llm: bool = False,
        model_name: str = "deepseek-chat",
        base_url: str = "https://api.deepseek.com",
        qpm: int = 5,
        output_dir: str = "./pipeline_output"
    ):
        
        self.topic = topic
        self.min_stars = min_stars
        self.max_repos = max_repos
        self.github_token = github_token
        self.use_llm = use_llm
        self.model_name = model_name
        self.base_url = base_url
        self.qpm = qpm
        self.output_dir = output_dir
        
        # 创建输出目录
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
            logger.info(f"创建输出目录: {self.output_dir}")
        
        # 生成文件名（带时间戳）
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        self.db_file = os.path.join(self.output_dir, f"results_{timestamp}.db")
        self.csv_file = os.path.join(self.output_dir, f"github_{topic}_repos_{timestamp}.csv")
        self.repos_file = os.path.join(self.output_dir, f"repos_{timestamp}.txt")
        self.results_file = os.path.join(self.output_dir, f"results_{timestamp}.txt")
        self.cache_dir = os.path.join(self.output_dir, 'cache')
        
    def step1_fetch_github_repos(self) -> bool:
        """步骤1: 获取GitHub仓库"""
        logger.info(f"=== 步骤1: 获取GitHub仓库 (topic:{self.topic}, stars>={self.min_stars}) ===")
        
        try:
            # 更新查询参数
            global query_params
            query_params["q"] = f"topic:{self.topic} stars:>={self.min_stars}"
            query_params["per_page"] = min(100, self.max_repos)
            
            # 获取仓库
            repositories = fetch_repositories()
            
            if not repositories:
                logger.error("未获取到任何仓库")
                return False
            
            # 限制仓库数量
            if len(repositories) > self.max_repos:
                repositories = repositories[:self.max_repos]
                logger.info(f"限制仓库数量为前 {self.max_repos} 个")
            
            # 保存CSV
            save_to_csv(repositories, self.csv_file)
            logger.info(f"CSV文件已保存: {self.csv_file}")
            
            # 生成repos.txt文件
            with open(self.repos_file, 'w', encoding='utf-8') as f:
                for repo in repositories:
                    f.write(f"{repo['name']} {repo['html_url']}\n")
            
            logger.info(f"仓库列表已保存: {self.repos_file}")
            logger.info(f"成功获取 {len(repositories)} 个仓库")
            return True
            
        except Exception as e:
            logger.error(f"步骤1失败: {str(e)}")
            return False
    
    def step2_analyze_pypi_packages(self) -> bool:
        """步骤2: 分析PyPI包名"""
        logger.info(f"=== 步骤2: 分析PyPI包名 (LLM: {self.use_llm}) ===")
        
        if not os.path.exists(self.repos_file):
            logger.error(f"仓库文件不存在: {self.repos_file}")
            return False
        
        try:
            FileProcessor.process_io(
                input_path=self.repos_file,
                output_path=self.results_file,
                token=self.github_token,
                use_llm=self.use_llm,
                model_name=self.model_name,
                base_url=self.base_url,
                qpm=self.qpm,
                save_to_db=True
            )
            
            logger.info(f"分析结果已保存: {self.results_file}")
            
            # 统计分析结果
            self._print_analysis_stats()
            return True
            
        except Exception as e:
            logger.error(f"步骤2失败: {str(e)}")
            return False
    
    def step3_install_packages(self, 
                              dry_run: bool = True, 
                              max_packages_per_repo: Optional[int] = None,
                              target_repos: Optional[list] = None,
                              use_uv: bool = True) -> bool:
        """步骤3: 安装PyPI包"""
        logger.info(f"=== 步骤3: 安装PyPI包 (dry_run: {dry_run}) ===")
        
        if not os.path.exists(self.results_file):
            logger.error(f"分析结果文件不存在: {self.results_file}")
            return False
        
        try:
            # 创建安装器
            installer = PyPiInstaller(
                dry_run=dry_run,
                verbose=True,
                use_uv=use_uv,
                cache_dir=self.cache_dir
            )
            
            # 解析结果文件
            repo_data = installer.parse_results_file(self.results_file)
            logger.info(f"发现 {len(repo_data)} 个仓库待处理")
            
            # 执行安装
            installer.install_all(
                repo_data=repo_data,
                target_repos=target_repos,
                max_packages_per_repo=max_packages_per_repo
            )
            
            return True
            
        except Exception as e:
            logger.error(f"步骤3失败: {str(e)}")
            return False
            
    def step4_verify_packages(self) -> bool:
        """步骤4: 验证PyPI包
        
        Args:
            verify_file (str): 验证文件路径,格式:
                - [包名] [GitHub名称] [GitHub仓库URL]
                - 示例：xx-reponse https://xx.github.com/xx [numpy, torch, tensorflow]
            output_dir (str, optional): 输出目录. Defaults to "./pipeline_output".
            use_llm (bool, optional): 是否使用LLM. Defaults to False.
            model_name (str, optional): LLM模型名称. Defaults to "DeepSeek-V3-Online-64K".
            qpm (int, optional): LLM每分钟查询次数. Defaults to 5.
            
        Returns:
            bool: 验证是否成功完成
        """
        logger.info(f"=== 步骤4: 验证PyPI包 ===")
        
        # 创建验证器
        verifier = AutoVerifier(
            output_dir=self.output_dir,
            use_llm=self.use_llm,
            model_name=self.model_name,
            base_url=self.base_url,
            qpm=self.qpm,
        )
    
        # 获取仓库和包名列表
        if self.results_file and os.path.exists(self.results_file):
            logger.info(f"从result.txt文件读取包名: {self.results_file}")
            try:
                with open(self.results_file, 'r', encoding='utf-8') as f:
                    for line_num, line in enumerate(f, 1):
                        line = line.strip()
                        if line and not line.startswith('#'):
                            # 使用正则表达式提取方括号内的内容
                            import re
                            match = re.search(r'\[(.*)\]', line)

                            if match:
                                # 提取GitHub名称和URL
                                parts = line.split()
                                if len(parts) >= 2:
                                    github_name = parts[0]
                                    github_url = parts[1]

                                    # 提取包列表
                                    package_list_str = match.group(1)
                                    # 分割包名并去除空白
                                    package_names = [pkg.strip() for pkg in package_list_str.split(',')]

                                    logger.info(f"处理仓库: {github_name}, GitHub URL: {github_url}")
                                    logger.info(f"包含的PyPI包: {', '.join(package_names)}")

                                    # 逐个验证仓库中的每个包
                                    for package_name in package_names:
                                        if package_name:  # 确保包名非空
                                            logger.info(f"开始验证仓库 {github_name} 中的包: {package_name}")
                                            verifier.verify_package(package_name)
                                        else:
                                            logger.warning(f"第{line_num}行发现空的包名，跳过")
                                else:
                                    logger.warning(f"第{line_num}行格式不正确，缺少GitHub名称或URL: {line}")
                            else:
                                # 尝试提取GitHub名称
                                parts = line.split()
                                if parts:
                                    github_name = parts[0]
                                    logger.warning(f"仓库 {github_name} (第{line_num}行) 没有找到PyPI包列表，跳过验证")
                                else:
                                    logger.warning(f"第{line_num}行无法解析: {line}")
            except Exception as e:
                logger.error(f"读取result.txt文件失败: {str(e)}")
                logger.error(traceback.format_exc())

            # 打印验证总结
            verifier._print_summary()

    def _print_analysis_stats(self):
        """打印分析统计信息"""
        if not os.path.exists(self.results_file):
            return
        
        total_repos = 0
        repos_with_packages = 0
        total_packages = 0
        
        with open(self.results_file, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                
                total_repos += 1
                
                # 简单解析包列表
                if '[' in line and ']' in line:
                    packages_part = line.split('[', 1)[1].split(']', 1)[0]
                    if packages_part.strip():
                        packages = [p.strip() for p in packages_part.split(',') if p.strip()]
                        if packages:
                            repos_with_packages += 1
                            total_packages += len(packages)
        
        logger.info(f"分析统计:")
        logger.info(f"  总仓库数: {total_repos}")
        logger.info(f"  有包的仓库: {repos_with_packages}")
        logger.info(f"  总包数: {total_packages}")
        if repos_with_packages > 0:
            logger.info(f"  平均每仓库包数: {total_packages/repos_with_packages:.1f}")
    
    def run_full_pipeline(self, 
                         dry_run: bool = True,
                         max_packages_per_repo: Optional[int] = None,
                         target_repos: Optional[list] = None,
                         use_uv: bool = True) -> bool:
        """运行完整流水线
        
        Args:
            dry_run: 是否只模拟安装而不实际安装
            max_packages_per_repo: 每个仓库最多安装的包数量
            target_repos: 指定要处理的仓库
            use_uv: 是否使用uv工具
            
        Returns:
            bool: 流水线是否成功完成
        """
        logger.info(f"=== 开始AI工具部署流水线 ===")
        logger.info(f"配置: topic={self.topic}, min_stars={self.min_stars}, max_repos={self.max_repos}")
        logger.info(f"输出目录: {self.output_dir}")
        
        # 步骤1: 获取GitHub仓库
        if not self.step1_fetch_github_repos():
            logger.error("流水线在步骤1失败")
            return False
        
        # 步骤2: 分析PyPI包名
        if not self.step2_analyze_pypi_packages():
            logger.error("流水线在步骤2失败")
            return False
        
        # 步骤3: 安装包
        installer = None
        try:
            # 创建安装器
            installer = PyPiInstaller(
                dry_run=dry_run,
                verbose=True,
                use_uv=use_uv
            )
            
            # 解析结果文件
            repo_data = installer.parse_results_file(self.results_file)
            logger.info(f"发现 {len(repo_data)} 个仓库待处理")
            
            # 执行安装
            installer.install_all(
                repo_data=repo_data,
                target_repos=target_repos,
                max_packages_per_repo=max_packages_per_repo
            )
            
        except Exception as e:
            logger.error(f"步骤3失败: {str(e)}")
            return False
        
        # 步骤4: 验证包（可选）
        if not self.step4_verify_packages():
            logger.error("流水线在步骤4失败")
            return False
        
        logger.info("=== 流水线执行完成 ===")
        return True


def main():
    parser = argparse.ArgumentParser(description="AI工具自动化部署流水线")
    
    # GitHub搜索参数
    parser.add_argument("--topic", default="ai", help="GitHub搜索主题 (默认: ai)")
    parser.add_argument("--min-stars", type=int, default=1000, help="最小star数 (默认: 1000)")
    parser.add_argument("--max-repos", type=int, default=1000, help="最大仓库数 (默认: 100)")
    
    # 分析参数
    parser.add_argument("--use-llm", default=True, action="store_true", help="使用LLM分析README")
    parser.add_argument("--model-name", default="deepseek/deepseek-chat-v3.1:free", help="使用的模型名称")
    parser.add_argument("--base-url", default="https://openrouter.ai/api/v1", help="API基础URL")
    parser.add_argument("--qpm", type=int, default=1, help="每分钟查询次数")
    
    # 安装参数
    parser.add_argument("--dry-run", action="store_true", help="仅模拟安装，不实际安装")
    parser.add_argument("--max-per-repo", type=int, help="每个仓库最多安装的包数量")
    parser.add_argument("--repos", nargs="+", help="指定要处理的仓库名称")
    parser.add_argument("--no-uv", action="store_true", help="不使用uv，回退到pip")
    parser.add_argument("--install-requirements", action="store_true", help="启用requirements文件安装")
    
    # 流水线控制
    parser.add_argument("--step", choices=["1", "2", "3", "4", "all"], default="all", 
                       help="执行特定步骤: 1=获取仓库, 2=分析包名, 3=安装包, 4=验证包, all=完整流水线")
    parser.add_argument("--output-dir", default="2025实战任务_作品文件夹/OpenCloudOS 9 AI软件自动化验证工具/张耀翔_作品/pipeline_output", help="输出目录")

    # 文件路径（用于单独执行步骤）
    parser.add_argument("--repos-file", default='2025实战任务_作品文件夹/OpenCloudOS 9 AI软件自动化验证工具/张耀翔_作品/pipeline_output/test_repos.txt', help="仓库列表文件路径（步骤2使用）")
    parser.add_argument("--results-file", default='2025实战任务_作品文件夹/OpenCloudOS 9 AI软件自动化验证工具/张耀翔_作品/pipeline_output/test_results.txt', help="分析结果文件路径（步骤3使用）")
    
    args = parser.parse_args()
    
    # 创建流水线实例
    pipeline = DeployPipeline(
        topic=args.topic,
        min_stars=args.min_stars,
        max_repos=args.max_repos,
        github_token=GITHUB_TOKEN,
        use_llm=args.use_llm,
        model_name=args.model_name,
        base_url=args.base_url,
        output_dir=args.output_dir
    )
    
    # 如果指定了外部文件，使用外部文件
    if args.repos_file:
        pipeline.repos_file = args.repos_file
    if args.results_file:
        pipeline.results_file = args.results_file
    
    # 执行指定步骤
    success = False
    
    if args.step == "1":
        success = pipeline.step1_fetch_github_repos()
    elif args.step == "2":
        success = pipeline.step2_analyze_pypi_packages()
    elif args.step == "3":
        success = pipeline.step3_install_packages(
            dry_run=args.dry_run,
            max_packages_per_repo=args.max_per_repo,
            target_repos=args.repos,
            use_uv=not args.no_uv
        )
    elif args.step == "4":
        success = pipeline.step4_verify_packages()
    elif args.step == "all":
        success = pipeline.run_full_pipeline(
            dry_run=args.dry_run,
            max_packages_per_repo=args.max_per_repo,
            target_repos=args.repos,
            use_uv=not args.no_uv
        )
    
    if success:
        logger.info("执行成功!")
        sys.exit(0)
    else:
        logger.error("执行失败!")
        sys.exit(1)


if __name__ == "__main__":
    main()

